/*
 * app_weather.c
 *
 *  Created on: 2022年9月5日
 *      Author: hehung
 */

#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"

#include "esp_tls.h"
#include "esp_crt_bundle.h"
#include "esp_http_client.h"
#include "cJSON.h"

#include "app_wifi.h"
#include "app_weather.h"
#include "app_aliyun.h"


#define TAG                        ("app_weather")

// 天气API使用的是https://www.tianqiapi.com/index/doc?version=day，
// 该API可以指定城市，如果不指定城市就是默认获取IP所在地址的天气信息
// >>>>其中appid以及appsecret是我的私人id以及密钥，请不要做他用<<<<
// >>>>其中appid以及appsecret是我的私人id以及密钥，请不要做他用<<<<
// >>>>其中appid以及appsecret是我的私人id以及密钥，请不要做他用<<<<
// 如果需要使用该API，请自行注册账号，每天免费获取500次。
#define WEB_WEA_SERVER             "v0.yiketianqi.com"
#define WEB_PORT                   "443"
#define WEB_WEA_HTTPS_URL          "https://v0.yiketianqi.com"
// 获取实时天气的地址
#define WEB_WEA_ONE_URL            "/free/day?appid=93524799&appsecret=atpHf1ye&unescape=1"
// 获取七日天气的地址
#define WEB_WEA_SEVENONE_URL       "/free/week?unescape=1&appid=93524799&appsecret=atpHf1ye"

// https请求 - 实时天气
static const char GET_REQUEST_WEA_ONE[] = "GET " WEB_WEA_ONE_URL " HTTP/1.0\r\n"
                             "Host: "WEB_WEA_SERVER ":" WEB_PORT "\r\n"
                             "User-Agent: esp-idf/1.0 esp32\r\n"
                             "\r\n";
// https请求 - 七日天气
static const char GET_REQUEST_WEA_SEVEN[] = "GET " WEB_WEA_SEVENONE_URL " HTTP/1.0\r\n"
                             "Host: "WEB_WEA_SERVER ":" WEB_PORT "\r\n"
                             "User-Agent: esp-idf/1.0 esp32\r\n"
                             "\r\n";


/* RGB port and pin */
#define WEATHER_GPIO_PORT      (0U)
#define WEATHER_GPIO_PIN       (45U)

#if (WEATHER_DEBUG == STD_ON)
//任务优先级
#define WEATHER_TASK_PRIO		(8U)
//任务堆栈大小	（单位为字，在32位处理器下，一个字等于4个字节）
#define WEATHER_TASK_SIZE 		(4096U)
//任务句柄
static TaskHandle_t Weather_TaskHandle;
#endif

#if 0
// 用于保存天气信息
static char weather_info[1024];
#else
static char *weather_info;
#endif

// 天气信息
static s_WeatherInfoType weather_curr;
static s_WeatherInfoSimpleType weather_seven[7];
static bool weather_get_succ_flg = false;

#if (WEATHER_DEBUG == STD_ON)
static void Weather_TaskCtrl(void *pvParameters);
#endif
static void Weather_HttpsGetRequest(const void *request, uint16_t request_len);
static void Weather_RealtimeInfoParse(const char *json_data);
static void Weather_SevenDayInfoParse(const char *json_data);

#if (WEATHER_DEBUG == STD_ON)
// 设置一个初始的时间
void Weather_TaskInit(void)
{
	portBASE_TYPE xReturn = pdPASS;

	// 初始化天气获取成功表示为空
	weather_curr.succ_flg = 0U;
	for (uint8_t i = 0; i < 7; i++)
	{
		weather_seven[i].succ_flg = 0U;
	}

	xReturn = xTaskCreatePinnedToCore(&Weather_TaskCtrl,
									"Weather_task",
									WEATHER_TASK_SIZE,
									NULL,
									WEATHER_TASK_PRIO,
									Weather_TaskHandle,
									CONFIG_ESP_MAIN_TASK_AFFINITY);
	if(pdPASS != xReturn)
		ESP_LOGE(TAG, "Weather task create failed!");

}
#endif

#if (WEATHER_DEBUG == STD_ON)
static void Weather_TaskCtrl(void *pvParameters)
{

	static uint16_t t_c = 900;
	weather_get_succ_flg = false;
	Weather_HttpsRequest();

	while (1)
	{
		// 每半个小时更新一次天气信息

		if ((0 != Wifi_GetConnectStatus()) && (t_c >= 900))
		{
			Weather_HttpsRequest();
			weather_get_succ_flg = true;
			t_c = 0;
		}

		t_c = (t_c >= 900) ? (900) : t_c + 1;

		vTaskDelay(pdMS_TO_TICKS(2000));
	}
}
#endif

// https请求天气信息
static void Weather_HttpsGetRequest(const void *request, uint16_t request_len)
{
    char buf[512];
    int ret, len;
    size_t written_bytes = 0;
    uint16_t weather_cnt = 0;
    struct esp_tls *tls;

    // https get请求，使用crt bundle方式
    ESP_LOGI(TAG, "https_request using crt bundle");
    esp_tls_cfg_t cfg = {
        .crt_bundle_attach = esp_crt_bundle_attach,
    };

    tls = esp_tls_conn_http_new(WEB_WEA_HTTPS_URL, &cfg);

    if (tls != NULL)
    {
        ESP_LOGI(TAG, "Connection established...");
    }
    else
    {
        ESP_LOGE(TAG, "Connection failed...");
        goto exit;
    }

    do
    {
        ret = esp_tls_conn_write(tls, request + written_bytes, request_len - written_bytes);
        if (ret >= 0)
        {
            ESP_LOGI(TAG, "%d bytes written", ret);
            written_bytes += ret;
        }
        else if (ret != ESP_TLS_ERR_SSL_WANT_READ  && ret != ESP_TLS_ERR_SSL_WANT_WRITE)
        {
            ESP_LOGE(TAG, "esp_tls_conn_write  returned: [0x%02X](%s)", ret, esp_err_to_name(ret));
            goto exit;
        }
    } while (written_bytes < request_len);

    ESP_LOGI(TAG, "Reading HTTP response...");

    do
    {
        len = sizeof(buf) - 1;
        bzero(buf, sizeof(buf));
        ret = esp_tls_conn_read(tls, (char *)buf, len);

        if (ret == ESP_TLS_ERR_SSL_WANT_WRITE  || ret == ESP_TLS_ERR_SSL_WANT_READ)
        {
            continue;
        }

        if (ret < 0)
        {
            ESP_LOGE(TAG, "esp_tls_conn_read  returned [-0x%02X](%s)", -ret, esp_err_to_name(ret));
            break;
        }
        else if (ret == 0)
        {
            ESP_LOGI(TAG, "connection closed");
            break;
        }

        len = ret;
        ESP_LOGD(TAG, "%d bytes read", len);
        /* Print response directly to stdout as it is read */
        for (int i = 0; i < len; i++)
        {
//            putchar(buf[i]);
            // 获取天气部分数据到buffer 'weather_info', 其他信息摒除
            if ((buf[i] == '{') || (weather_cnt > 0U))
            {
            	weather_info[weather_cnt++] = buf[i];
            }
        }
//        putchar('\n'); // JSON output doesn't have a newline at end
    } while (1);

    // 添加字符串结束符
    weather_info[weather_cnt] = '\0';
#if (WEATHER_DEBUG == STD_ON)
    ESP_LOGI(TAG, "Weather Info: %s", weather_info);
#endif
exit:
	weather_cnt = 0;
    esp_tls_conn_delete(tls);
}

// 获取天气信息请求
void Weather_HttpsRequest(void)
{
	// 初始化天气获取成功表示为空
	weather_curr.succ_flg = 0U;
	for (uint8_t i = 0; i < 7; i++)
	{
		weather_seven[i].succ_flg = 0U;
	}

	weather_info = (char *)malloc(1024);

	Weather_HttpsGetRequest(GET_REQUEST_WEA_ONE, sizeof(GET_REQUEST_WEA_ONE));
	Weather_RealtimeInfoParse(weather_info);

	Weather_HttpsGetRequest(GET_REQUEST_WEA_SEVEN, sizeof(GET_REQUEST_WEA_SEVEN));
	Weather_SevenDayInfoParse(weather_info);

	free(weather_info);
}

// 解析天气数据
static void Weather_RealtimeInfoParse(const char *json_data)
{
	cJSON* root = cJSON_Parse(json_data);;

	cJSON* cjson_wea_curr = cJSON_GetObjectItem(root, "wea");
	cJSON* cjson_temp_curr = cJSON_GetObjectItem(root, "tem");
	cJSON* cjson_temp_high = cJSON_GetObjectItem(root, "tem_day");
	cJSON* cjson_temp_low = cJSON_GetObjectItem(root, "tem_night");
	cJSON* cjson_air = cJSON_GetObjectItem(root, "air");
	cJSON* cjson_pressure = cJSON_GetObjectItem(root, "pressure");
	cJSON* cjson_humi = cJSON_GetObjectItem(root, "humidity");

	// 先清空buffer，再往里面填充数据
	memset(weather_curr.weather, '\0', sizeof(weather_curr.weather));
	memset(weather_curr.temp, '\0', sizeof(weather_curr.temp));
	memset(weather_curr.temp_h, '\0', sizeof(weather_curr.temp_h));
	memset(weather_curr.temp_l, '\0', sizeof(weather_curr.temp_l));
	memset(weather_curr.air, '\0', sizeof(weather_curr.air));
	memset(weather_curr.pressure, '\0', sizeof(weather_curr.pressure));
	memset(weather_curr.humi, '\0', sizeof(weather_curr.humi));

	sscanf(cjson_wea_curr->valuestring, "%s", weather_curr.weather);
	sscanf(cjson_temp_curr->valuestring, "%s", weather_curr.temp);
	sscanf(cjson_temp_high->valuestring, "%s", weather_curr.temp_h);
	sscanf(cjson_temp_low->valuestring, "%s", weather_curr.temp_l);
	sscanf(cjson_air->valuestring, "%s", weather_curr.air);
	sscanf(cjson_pressure->valuestring, "%s", weather_curr.pressure);
	sscanf(cjson_humi->valuestring, "%s", weather_curr.humi);

	weather_curr.succ_flg = 1U;

#if (WEATHER_DEBUG == STD_ON)
	printf("当前天气:%s\n", weather_curr.weather);
	printf("当前温度:%s\n", weather_curr.temp);
	printf("最高温度:%s\n", weather_curr.temp_h);
	printf("最低温度:%s\n", weather_curr.temp_l);
	printf("空气质量1:%s\n", cjson_air->valuestring);
	printf("空气质量:%s\n", weather_curr.air);
	printf("大气压强:%s\n", weather_curr.pressure);
	printf("当前湿度:%s\n", weather_curr.humi);
#endif
}

// 解析天气数据
static void Weather_SevenDayInfoParse(const char *json_data)
{
	cJSON* root = cJSON_Parse(json_data);
	cJSON* cjson_data= cJSON_GetObjectItem(root, "data");

	for (int i = 0; i < cJSON_GetArraySize(cjson_data); i++)
	{
		cJSON *cjson_array = cJSON_GetArrayItem(cjson_data, i);

		cJSON *cjson_date =  cJSON_GetObjectItem(cjson_array, "date");
		cJSON *cjson_wea =  cJSON_GetObjectItem(cjson_array, "wea");
		cJSON *cjson_temp_high =  cJSON_GetObjectItem(cjson_array, "tem_day");
		cJSON *cjson_temp_low =  cJSON_GetObjectItem(cjson_array, "tem_night");

		memset(weather_seven[i].date, '\0', sizeof(weather_seven[i].date));
		memset(weather_seven[i].weather, '\0', sizeof(weather_seven[i].weather));
		memset(weather_seven[i].temp_h, '\0', sizeof(weather_seven[i].temp_h));
		memset(weather_seven[i].temp_l, '\0', sizeof(weather_seven[i].temp_l));

		sscanf(cjson_date->valuestring, "%s", weather_seven[i].date);
		sscanf(cjson_wea->valuestring, "%s", weather_seven[i].weather);
		sscanf(cjson_temp_high->valuestring, "%s", weather_seven[i].temp_h);
		sscanf(cjson_temp_low->valuestring, "%s", weather_seven[i].temp_l);

		weather_seven[i].succ_flg = 1U;

#if (WEATHER_DEBUG == STD_ON)
		printf("后%d天日期:%s\n", i, weather_seven[i].date);
		printf("后%d天天气:%s\n", i, weather_seven[i].weather);
		printf("后%d天温度:%s\n", i, weather_seven[i].temp_h);
		printf("后%d天温度:%s\n", i, weather_seven[i].temp_l);
#endif
	}
}

s_WeatherInfoType Weather_GetCurrentWea(void)
{
	return weather_curr;
}

s_WeatherInfoSimpleType* Weather_GetSevenWea(void)
{
	return weather_seven;
}

bool Weather_GetSuccessFlag(void)
{
	return weather_get_succ_flg;
}
